﻿using AutoMapper;
using Hsp.WebNovels.Model;
using Hsp.WebNovels.Model.Datum;
using Hsp.WebNovels.Model.Dtos;
using Hsp.WebNovels.Model.Models;
using Hsp.WebNovels.Web.Models;
using Hsp.WebNovels.Web.Utilities;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections;
using System.Text;
using System.Web;

namespace Hsp.WebNovels.Web.Controllers
{
    public class ChapterController : BaseController
    {
        #region 控制器初始化

        private readonly IMapper _mapper;
        private readonly ILogger<ChapterController> _logger;
        private readonly NovelsContext _context;

        /// <summary>
        /// 章节控制器构造函数
        /// </summary>
        public ChapterController(ILogger<ChapterController> logger, NovelsContext context, IMapper mapper) : base(logger, context, mapper)
        {
            this._logger = logger;
            this._mapper = mapper;
            this._context = context;
        }

        #endregion

        /// <summary>
        /// 章节管理
        /// </summary>
        /// <param name="novelId">章节ID</param>
        /// <param name="novelName">章节名称</param>
        /// <param name="webId">小说ID</param>
        /// <param name="webName">小说名称</param>
        /// <returns></returns>
        public ActionResult Index(string novelId, string novelName, string webId, string webName)
        {
            var model = new ChapterViewModel();
            model.NovelId = novelId;
            model.NovelName = novelName ?? "章节管理";
            model.WebId = webId;
            model.WebName = webName ?? "小说管理";

            return View(model);
        }

        // POST: Chapter/Delete/5
        [HttpPost, ActionName("Delete")]
        public async Task<IActionResult> DeleteConfirmed(string id)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();

            try
            {
                if (_context.Chapters == null)
                {
                    return Problem("Entity set 'WisdomRecyclingContext.RecyclingSites' is null.");
                }
                var Chapter = await _context.Chapters.FindAsync(id);
                if (Chapter != null)
                {
                    await Db.Deleteable<Content>(id).ExecuteCommandAsync();
                    _context.Chapters.Remove(Chapter);
                }

                await _context.SaveChangesAsync();
                await transaction.CommitAsync(); // 事务提交
                return Json(new { success = true, message = "章节信息删除成功" });
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync(); // 事务回滚
                _logger.LogError($"{nameof(DeleteConfirmed)}：{ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// 批量删除数据
        /// POST: Chapter/BatchDelete/5,7
        /// </summary>
        /// <param name="id">数据编号集合</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> BatchDelete(string? id)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();

            try
            {
                if (string.IsNullOrEmpty(id))
                {
                    return Json(new { success = false, Message = "要删除的章节信息数据主键集合为空！" });
                }
                var idarr = id.Trim().Split(',');
                foreach (var idd in idarr)
                {
                    var Chapter = await _context.Chapters.FindAsync(idd);
                    if (Chapter != null)
                    {
                        await Db.Deleteable<Content>(idd).ExecuteCommandAsync();
                        _context.Chapters.Remove(Chapter);
                    }
                }
                await _context.SaveChangesAsync();
                await transaction.CommitAsync(); // 事务提交
                return Json(new { success = true, message = "章节信息数据删除成功！" });
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync(); // 事务回滚
                _logger.LogError($"{nameof(BatchDelete)}：{ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        private bool ChapterExists(string? id)
        {
            return _context.Chapters.Any(e => e.Id == id);
        }


        #region 章节信息列表数据视图

        /// <summary>
        /// 章节信息列表数据视图
        /// GET: Chapter/TableList
        /// </summary>
        /// <param name="mid">菜单ID</param>
        /// <param name="mapname">菜单路径</param>
        /// <returns></returns>
        public IActionResult TableList(int? mid, string? mapname)
        {
            ChapterDto dto = new();
            var model = _mapper.Map<ChapterViewModel>(dto);
            return View(model);
        }

        #endregion 

        #region 章节信息分页数据查询

        /// <summary>
        /// 章节信息分页数据查询
        /// GET: Chapter/PageList
        /// </summary>
        /// <param name="pageNumber">当前页码</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="title">检索关键词</param>
        /// <param name="novelId">小说ID</param>
        /// <returns></returns>
        public JsonResult PageList(int? pageNumber, int? pageSize, string? title, string? startDate, string? endDate, string? novelId)
        {
            try
            {
                #region 参数处理

                var urlDecode = HttpUtility.UrlDecode(title);
                if (urlDecode != null) title = title == null ? "" : urlDecode.Trim();

                string strQry = "";

                if (!string.IsNullOrEmpty(title))
                {
                    //strQry += string.Format(@" AND (c.Title LIKE '%{0}%' OR c.ABCDEF LIKE '%{0}%')", title);
                }
                if (!string.IsNullOrEmpty(novelId))
                {
                    strQry += string.Format(@" AND (c.NovelId = '{0}')", novelId);
                }
                // 起止日期
                if (!string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate))
                {
                    strQry += string.Format(@" AND(unix_timestamp(c.`CreateTime`) between unix_timestamp('{0}') and unix_timestamp(DATE_ADD('{1}', INTERVAL 1 DAY)))", startDate, endDate);
                }
                if (!string.IsNullOrEmpty(startDate) && string.IsNullOrEmpty(endDate))
                {
                    strQry += string.Format(@" AND(unix_timestamp(c.`CreateTime`) > unix_timestamp('{0}'))", startDate, endDate);
                }
                if (string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate))
                {
                    strQry += string.Format(@" AND(unix_timestamp(c.`CreateTime`) < unix_timestamp(DATE_ADD('{1}', INTERVAL 1 DAY)))", startDate, endDate);
                }

                #endregion

                #region 分页及页码数据处理处理

                if (!pageNumber.HasValue) pageNumber = 1;
                if (!pageSize.HasValue) pageSize = 10;

                var skip = (pageNumber.Value - 1) * pageSize.Value;

                var min = (pageNumber - 1) * pageSize + 1;
                var max = pageNumber * pageSize;

                #endregion

                var strSql = string.Format(@"
	                ;WITH PageTb AS (
		                SELECT ROW_NUMBER() OVER (ORDER BY ChapterIdx) RowNumber
			                , Id,NovelId,ChapterUrl,NextUrl,SourceTitle,ChapterTitle,ChapterIdx,ChapterName,HeadWord,WordCount,ValidChapter,CreateTime,UpdateTime
		                FROM dbo.[Chapters] c WHERE (1 = 1) 
		                 {2} 
		                )
	                SELECT * 
	                FROM PageTb a
	                CROSS JOIN (SELECT MAX(RowNumber) AS RecordCount FROM PageTb) AS b 
	                WHERE (a.RowNumber BETWEEN {0} AND {1});
                 ", min, max, strQry);

                var list = Db.Ado.SqlQuery<ChapterDto>(strSql);
                //_context.Database.SqlQuery<ChapterDto>(strSql)?.ToList();

                return Json(new { success = true, total = list?.Count > 0 ? list[0].RecordCount : 0, rows = list });
            }
            catch (Exception ex)
            {
                _logger.LogError($"{nameof(PageList)}：{ex}");
                return Json(new { success = false, total = 0, rows = new List<Hashtable>(), message = ex.Message });
            }
        }

        #endregion

        #region 章节信息数据保存

        /// <summary>
        /// 章节信息数据保存
        /// POST: Chapter/Save
        /// </summary>
        /// <param name="chapter">章节信息</param>
        /// <returns></returns>
        public async Task<IActionResult> Save([FromBody] Chapter chapter)
        {
            try
            {
                string flag;
                if (string.IsNullOrEmpty(chapter.Id) || !ChapterExists(chapter.Id))
                {
                    flag = "添加";
                    chapter.Id = Guid.NewGuid().ToString().ToUpper();
                    chapter.CreateTime = DateTime.Now;
                    _context.Add(chapter);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    flag = "修改";
                    chapter.UpdateTime = DateTime.Now;
                    //_context.Update(Chapter);
                    await Db.Updateable(chapter).ExecuteCommandAsync();
                }

                return Json(new { success = true, message = $"章节信息数据{flag}成功!" });

            }
            catch (Exception ex)
            {
                _logger.LogError($"{nameof(Save)}：{ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        #endregion

        #region 小说抓取内容

        /// <summary>
        /// 小说抓取内容
        /// GET: Chapter/Crawl
        /// </summary>
        /// <param name="id">小说ID</param>
        /// <returns></returns>
        public async Task<IActionResult> Crawl(string id)
        {
            try
            {
                //await Task.Delay(0);

                #region 数据检查与获取

                if (string.IsNullOrEmpty(id))
                {
                    throw new Exception("小说ID为空");
                }
                var novel = await _context.Novels.FindAsync(id);
                if (novel == null)
                {
                    throw new Exception("小说数据为空");
                }
                var web = await _context.WebSites.FindAsync(novel.WebId);
                if (web == null)
                {
                    throw new Exception("站点数据为空");
                }

                var webUrl = web.WebUrl;
                if (string.IsNullOrEmpty(webUrl))
                {
                    throw new Exception("站点地址为空");
                }
                var encoding = web.Encoding;
                if (string.IsNullOrEmpty(encoding))
                {
                    throw new Exception("站点编码为空");
                }

                var http = webUrl.Split(':')[0];
                if (string.IsNullOrEmpty(http))
                {
                    throw new Exception("地址协议为空");
                }

                //var isWebSiteUrlCombine = true; // 是否站点地址合并
                //var isNovelUrlCombine = true; // 是否小说地址合并
                var isGetText = web.AnnotationType == 0; // 正文类型：0-文本；1-HTML；

                var novelUrl = novel.NovelUrl;
                if (string.IsNullOrEmpty(novelUrl))
                {
                    throw new Exception("小说地址为空");
                }
                var xpathAddress = web.AddressObject;
                if (string.IsNullOrEmpty(xpathAddress))
                {
                    throw new Exception("小说地址对象为空");
                }
                var xpathAddressNext = web.AddressNext;
                var xpathContent = web.ContentObject;
                if (string.IsNullOrEmpty(xpathContent))
                {
                    throw new Exception("小说内容对象为空");
                }
                var xpathContentNext = web.ContentNext;

                #endregion

                System.Console.WriteLine("**********************开始抓取章节地址**********************");

                #region 1) 读取小说地址

                await GetChapterUrlAsync(web, novel);

                #endregion

                System.Console.WriteLine("**********************完成抓取章节地址**********************");
                System.Console.WriteLine("**********************开始抓取章节内容**********************");

                #region 2) 获取小说内容

                List<Chapter> chapters = await Db.Queryable<Chapter>()
                    .Where(c => c.NovelId == id).ToListAsync();

                await Parallel.ForEachAsync(chapters, new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 8
                }, async (chapter, _) =>
                {
                    if (string.IsNullOrEmpty(chapter.ChapterUrl)) return;

                    var client = SqlSugarBase.GetClientInstance();

                    await GetNovelContentAsync(client, encoding, webUrl, xpathContent, xpathContentNext, chapter, isGetText);

                });

                #endregion

                System.Console.WriteLine("**********************完成抓取章节内容**********************");

                #region 小说章节总数更新

                var strSql = string.Format(@"
                    UPDATE [dbo].[Novels]
                    SET ChapterCount = (SELECT COUNT(*) FROM [dbo].[Chapters] WHERE NovelId = n.Id)
                    , UpdateTime = GETDATE()
                    FROM [dbo].[Novels] n
                    WHERE (n.Id = '{0}');
                        ;", id);

                await Db.Ado.ExecuteCommandAsync(strSql);

                #endregion

                return Json(new { success = true, message = "小说章节获取成功" });
            }
            catch (Exception ex)
            {
                _logger.LogError($"{nameof(Crawl)}：{ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        /// <summary>
        /// 获取小说地址信息
        /// </summary>
        /// <param name="novel">小说信息</param>
        /// <param name="web">站点信息</param>
        /// <param name="webUrl"></param>
        /// <param name="encoding"></param>
        /// <param name="http"></param>
        /// <param name="novelUrl"></param>
        /// <param name="xpathAddress">地址对象</param>
        /// <param name="xpathAddressNext">下一页地址对象</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private static async Task GetChapterUrlAsync(WebSite? web, Novel? novel)
        {
            var webUrl = web.WebUrl; // 站点地址
            var encoding = web.Encoding; // 内容编码
            var http = webUrl.Split(':')[0]; // 传输协议
            var xpathAddress = web.AddressObject; // 地址对象
            var xpathAddressNext = web.AddressNext; // 下一页地址对象
            var novelUrl = novel.NovelUrl; // 小说目录页面地址

            //通过xpath指定页面节点获取节点中对应的内容
            HtmlDocument? htmlAddress = await HtmlDocAsync(novelUrl, encoding);
            if (htmlAddress == null)
            {
                throw new Exception("获取小说地址代码数据为空");
            }

            //xpathAddress = "//div[@class='section-box'][last()]/ul/li/a";
            //xpathAddress = "//ul[@class='section-list']/li";
            //xpathAddress = "//ul[@class='section-list']/li/a";

            var nodes = htmlAddress.DocumentNode.SelectNodes(xpathAddress);

            if (nodes == null)
            {
                throw new Exception("获取小说地址节点数据为空");
            }

            foreach (var item in nodes)
            {
                //System.Console.WriteLine($"{item.InnerText}");
                //System.Console.WriteLine($"{item.InnerText.Trim()}，{item.Attributes["href"].Value.Trim()}");

                #region 地址处理

                // 地址是否需要组合：0-不需要；1-网站地址 + 章节地址；2-小说地址 + 章节地址；
                var chapterUrl = item.Attributes["href"].Value.Trim();
                if (!chapterUrl.StartsWith("/")) chapterUrl = "/" + chapterUrl;

                if (web.UrlCombine == 1)
                {
                    chapterUrl = $"{webUrl.TrimEnd('/')}{chapterUrl}";
                }
                if (web.UrlCombine == 2)
                {

                    chapterUrl = $"{novelUrl.TrimEnd('/')}{chapterUrl}";
                }

                if (!chapterUrl.ToLower().StartsWith("http"))
                {
                    chapterUrl = http + ":" + chapterUrl;
                }

                #endregion

                var chapter = await Db.Queryable<Chapter>()
                    .Where(c => c.ChapterUrl == chapterUrl).FirstAsync();

                var exists = chapter != null;

                if (!exists || chapter == null)
                {
                    chapter = new();
                    chapter.Id = Guid.NewGuid().ToString().ToUpper();
                }

                #region 章节数据

                var title = item.InnerText;
                chapter.SourceTitle = title;
                ChapterTitle chapterTitle = TitleCleaning(title);
                chapter.ChapterIdx = chapterTitle.Idx;
                chapter.ChapterName = chapterTitle.Name;
                chapter.HeadWord = chapterTitle.HeadWord;
                chapter.ChapterTitle = chapterTitle.Title;

                #endregion

                chapter.NovelId = novel.Id; // 小说ID
                chapter.ChapterUrl = chapterUrl;

                #region MyRegion

                //HtmlDocument htmlContent = HtmlDoc(chapterUrl, encoding);
                //var contentNode = htmlContent.DocumentNode.SelectSingleNode(xpathContent);
                //var content = contentNode.InnerHtml;
                //content = ContentCleaning(content);

                //var nextUrl = GetNextUrl(htmlContent, xpathNextPage, webSite);

                //chapter.NextUrl = nextUrl;

                //if (!string.IsNullOrEmpty(nextUrl))
                //{
                //    var nextContent = GetNextContents(nextUrl, encoding, xpathContent, xpathNextPage, webSite);
                //    content += nextContent;
                //}

                //chapter.WordCount = content.Length;
                //chapter.ValidChapter = content.Length > 500 ? 1 : 0;

                #region 小说内容保存

                //Content chapterContent = new();
                //chapterContent.ChapterId = chapter.Id;
                //chapterContent.NovelId = chapter.NovelId;
                //chapterContent.Contents = content;

                //db.Insertable(chapterContent).ExecuteCommand();

                #endregion

                #endregion

                if (exists)
                {
                    chapter.UpdateTime = DateTime.Now;
                    await Db.Updateable(chapter).ExecuteCommandAsync();
                }
                else
                {
                    chapter.CreateTime = DateTime.Now;
                    await Db.Insertable(chapter).ExecuteCommandAsync();
                }

                System.Console.WriteLine(chapter.ChapterTitle);
            }

            if (!string.IsNullOrEmpty(xpathAddressNext))
            {
                var nextNode = htmlAddress.DocumentNode.SelectSingleNode(xpathAddressNext);
                var href = nextNode.Attributes["href"];
                if (href == null) return;
                var nextUrl = href.Value.Trim();
                if (!string.IsNullOrEmpty(nextUrl))
                {
                    novel.NovelUrl = nextUrl;
                    await GetChapterUrlAsync(web, novel);
                }
            }
        }

        #endregion

        #region 小说内容清空

        /// <summary>
        /// 小说内容清空：可清空单个，多个或者全部
        /// GET: Chapter/Clear
        /// </summary>
        /// <param name="id">小说ID</param>
        /// <returns></returns>
        public async Task<IActionResult> Clear(string? id)
        {
            try
            {
                #region 全部清空

                if (string.IsNullOrEmpty(id))
                {
                    throw new Exception("小说ID为空");
                }

                #endregion

                await Db.Deleteable<Content>().Where(c => c.NovelId == id).ExecuteCommandAsync();
                await Db.Deleteable<Chapter>().Where(c => c.NovelId == id).ExecuteCommandAsync();

                return Json(new { success = true, message = "小说内容清空成功" });
            }
            catch (Exception ex)
            {
                _logger.LogError($"{nameof(Clear)}：{ex}");
                return Json(new { success = false, message = ex.Message });
            }
        }

        #endregion
    }
}
